from redis_db import RedisSession, get_time, create_token

import random
import config
import query

import json

sockets = dict()  # use for server push


class UserCache:
    def __init__(self):
        #  using int 0/1 save bool
        self.waiting_users = RedisSession('waiting')  # {username: score}
        self.partner_users = RedisSession('partner')  # {username: [partner_name, self_ready]}
        self.playing_users = RedisSession('playing')  # {username: [partner_name,  role]}
        self.online_users = RedisSession('online')  # {username: [last_action, user_token]}

cache_session = UserCache()


def get_user_token(username):
    return create_token(username)


def can_login_in(username):
    result = cache_session.online_users.get(username)
    if result is None:
        return True  # has not signed in
    last_time = result[0]
    if get_time() - float(last_time) > config.TIME_OUT:
        return True  # time out connect closed
    return False


def get_first_partner(username):
    for name in cache_session.waiting_users:
        if name != username:
            cache_session.waiting_users.pop(name)
            cache_session.waiting_users.pop(username)
            cache_session.partner_users.set(username, [name, 0])
            cache_session.partner_users.set(name, [username, 0])
            return name
    return None


def get_new_partner(username, partner):
    for name in cache_session.waiting_users:
        if name != username and name != partner:
            cache_session.waiting_users.pop(name)
            cache_session.partner_users.set(username, [name, 0])
            cache_session.partner_users.set(name, [username, 0])
            return name
    return None


def partner_leave(username):  # when partner leave, use this to get a new one
    cache_session.partner_users.pop(username)
    success, failure, score = query.get_history(username)
    cache_session.waiting_users.set(username, score)
    return get_first_partner(username)


def start_game(username, partner):
    cache_session.partner_users.pop(username)
    cache_session.partner_users.pop(partner)
    role = random.randint(0, 1)  # first:1, second:0
    cache_session.playing_users.set(username, [partner, role])
    cache_session.playing_users.set(partner, [username, - role])
    return role


def end_game(username, partner):
    cache_session.playing_users.pop(username)
    cache_session.playing_users.pop(partner)
    cache_session.partner_users.set(username, [partner, 0])
    cache_session.partner_users.set(partner, [username, 0])


def force_offline(username):
    cache_session.online_users.pop(username)
    #  waiting users
    user = cache_session.waiting_users.get(username)
    if user:
        cache_session.waiting_users.pop(username)
        return 1  # status: waiting
    # partner users
    user = cache_session.partner_users.get(username)
    if user:
        partner = user[0]
        partner_leave(partner)
        cache_session.partner_users.pop(username)
        return 2  # status: partner
    # playing users
    user = cache_session.playing_users.get(username)
    if user:
        partner = user[0]
        end_game(username, partner)
        query.resign_update_history(username, partner)
        # push resign
        socket = sockets[username]
        values = {
            'action': 'resign',
            'result': 'ok',
        }
        response = json.dumps(values)
        socket.write_message(response)
        return 3  # status: playing
    return 0  # status: unknown


def login_required(method):
    def wrapper(request):
        # get args
        username = request.get('username')
        token = request.get('token')
        if (username is None) or (token is None):
            return {'result': 'ERROR_BAD_REQUEST'}
        user = cache_session.online_users.get(username)
        # check token
        if (user is None) or (token != user[1]):
            return {'result': 'ERROR_USER_OFFLINE'}
        # check timeout
        if get_time() - user[0] < config.TIME_OUT:
            user[0] = get_time()
            cache_session.online_users.set(username, user)
            return method(request)
        return {'result': 'ERROR_USER_OFFLINE'}
    return wrapper
